using System.Collections.Generic;
using System.IO;
using Tycho.Runtime;

namespace Tycho.Compiler {
    public class ModuleSymbolScope : SymbolScope {
        Dictionary<AnyObject, HashSet<Symbol>> Modules;
        private IModuleScopeLoader ModuleLoader;

        public ModuleSymbolScope (IModuleScopeLoader moduleLoader) {
            Modules = new Dictionary<AnyObject, HashSet<Symbol>> ();
            ModuleLoader = moduleLoader;
        }

        public override int FindFrameIndex (AnyObject name, int index) {
            Symbol symbol = name as Symbol;

            if (symbol == null) {
                return SymbolNotFound;
            }

            HashSet<Symbol> symbols;

            if (GetModule (symbol.Namespace, out symbols)) {
                if (symbols.Contains (symbol)) {
                    return index;
                } else {
                    return SymbolNotFound;
                }
            } else {
                return SymbolNotFound;
            }
        }

        public override string PrettyPrint () {
            var output = new StringWriter ();

            foreach (HashSet<Symbol> module in Modules.Values) {
                foreach (Symbol sym in module) {
                    output.WriteLine (sym + ", " + sym.GetHashCode ());
                }
            }

            return output.ToString ();
        }

        private bool GetModule (Namespace ns, out HashSet<Symbol> symbols) {
            if (Modules.TryGetValue (ns, out symbols)) {
                return true;
            } else {
                symbols = ModuleLoader.LoadModule (ns);

                if (symbols != null) {
                    Modules.Add (ns, symbols);
                    return true;
                } else {
                    return false;
                }
            }
        }

        public void DeclareModule (AnyObject module) {
            AnyObject ns = module.GetProperty (Symbols.RuntimeNamespace);

            Modules [ns] = GetVariableNames (module, ns);
        }

        public static HashSet<Symbol> GetVariableNames (AnyObject module, AnyObject ns) {
            HashSet<Symbol> symbols = new HashSet<Symbol> ();

            foreach (AnyObject variable in module.Variables) {
                Symbol sym = variable.Expect<Symbol> ();

                if (sym.Namespace != ns) {
                    throw new TychoException ("symbol " + sym + " not in module namespace " + ns);
                }

                symbols.Add (sym);
            }

            return symbols;
        }
    }
}